﻿package net.sapfree.core.db;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import net.sapfree.core.db.dsp.DefaultDataSourceProvider;
import net.sapfree.core.db.dsp.TomcatDataSourceProvider;
/**
 * 提供数据库连接的工具类，该类由DBConnectionProvider替代
 * @author qiuzhl
 * @deprecated
 */
public class DBConnectionLocator
{
	private static Logger logger = Logger.getLogger(DBConnectionLocator.class);
	/**
	 * 单例模式，保存唯一实例的引用
	 */
	private static DBConnectionLocator ref_obj;
	
	/**
	 * 数据源提供类的实例引用，通过该类从应用服务器获取DataSource实例
	 */
	private static DataSourceProvider m_dsp = null;
	
	/**
	 * 实现单实例引用变量ref_obj互斥访问的锁
	 */
	private static Object ref_obj_lock = new Object();
	
	/**
	 * 上下文对象
	 */
	private Context m_context ;
	
	/**
	 * 保存数据源JNDI名称和DataSource对象的映射关系，避免每次都从应用服务器获取
	 */
	private Map<String,DataSource> ds_jdni_datasource_Map = new ConcurrentHashMap<String,DataSource>();
	
	/**
	 * 配置文件路径，common_component.xml
	 */
	private static String m_sConfigFileName = "common_component.xml";
	
	/**
	 * 配置文件中，数据源配置的节点元素
	 */
	private Node m_configElement = null;
	
	/**
	 * 通过该方法获取类的唯一实例
	 * @return
	 * @throws NamingException
	 */
	public static DBConnectionLocator getInstance()
	{
		if(ref_obj == null)
		{//这里使用了双重检查
			synchronized(ref_obj_lock)
			{
				if(ref_obj == null)
				{
					ref_obj = new DBConnectionLocator();
				}
			}
		}
		return ref_obj;
	}
	
	/**
	 * 由于是单例模式，包含唯一的私有构造方法
	 * @throws DBConnectionException 
	 * @throws NamingException
	 */
	protected DBConnectionLocator()
	{
		try
		{
			initConfigElement();
			initLocalDataSource();
			initContext();
			initDataSourceProvider();
		}
		catch(Exception e)
		{
			//e.printStackTrace();
		}		
	}
	
	/**
	 * 从XML配置文件中读取数据源相关配置，保存在成员变量m_configElement中，供后续方法使用
	 */
	private void initConfigElement()
	{
		InputStream is = null;
		try 
		{
			//使用w3c的dom解析xml
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
			factory.setValidating(false); 
			DocumentBuilder builder = factory.newDocumentBuilder();
			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			is = ccl.getResourceAsStream(m_sConfigFileName);
			if(is == null)
			{
				logger.warn("can not find datasource config file " + m_sConfigFileName + ",maybe it is missing,please make sure it exists in classpath.");
				return ;
			}
			Document document = builder.parse(is); 
			Element root = document.getDocumentElement(); 
			if(root == null)
			{
				logger.warn("the config file " + m_sConfigFileName + " has no root element,maybe it is damaged.");
				return ;
			}
			//使用xpath查找数据源配置的节点，并将该元素保存到m_configElement中
			XPathFactory xpf = XPathFactory.newInstance();
            XPath xpath = xpf.newXPath();
            m_configElement = (Node) xpath.evaluate("DBConnectionLocator", root,XPathConstants.NODE);
			if(m_configElement == null)
			{
				logger.warn("the config file " + m_sConfigFileName + " has none element about datasource.");
				return ;
			}
		}
		catch(Exception e)
		{
			logger.error("something is wrong when load the config,error message is " + e.getMessage());
		}
		finally
		{
			try
			{
				if(is != null)	is.close();
			} 
			catch (Exception e)
			{
				//do nothing
			}
		}
	}
	
	private void initContext() throws NamingException
	{
		Properties props = null;
		if(m_configElement != null)
		{
			XPathFactory xpf = XPathFactory.newInstance();
	        XPath xpath = xpf.newXPath();
	        
	        try
	        {
				Node contextInitial = (Node) xpath.evaluate("InitialContext", m_configElement,XPathConstants.NODE);
				props = getProperties(contextInitial);
			}
	        catch (XPathExpressionException e)
			{
				e.printStackTrace();
			}
		}
        m_context = new InitialContext(props);

	}
	
	private Properties getProperties(Node node)
	{
		Properties properties = new Properties();
		if(node == null) return properties;
        NodeList propNodes = node.getChildNodes();        
        for(int j=0;j<propNodes.getLength();j++)
        {
        	Node propNode = propNodes.item(j);
	       	if(Node.ELEMENT_NODE != propNode.getNodeType()) continue;
	       	Node text = propNode.getFirstChild();
	       	properties.setProperty(propNode.getNodeName(), text == null ? "":text.getNodeValue());
        }
        return properties;
	}
	
	private void initDataSourceProvider() throws NamingException
	{
		Context envContext = (Context) m_context.lookup("");
		String ins_name = envContext.getClass().getName();
		
		//WAS:ins_name=com.ibm.ws.naming.jndicos.CNContextImpl
		//HOST:ins_name=com.sun.jndi.rmi.registry.RegistryContext
		//TOMCAT:ins_name=org.apache.naming.NamingContext
		
		//应用服务器为TOMCAT
		if("org.apache.naming.NamingContext".equals(ins_name))
		{
			m_dsp = new TomcatDataSourceProvider();
		}
		else
		{
			m_dsp = new DefaultDataSourceProvider();
		}
	}
	
	private void initLocalDataSource()
	{
		try 
		{ 
			if(m_configElement == null) return ;
			XPathFactory xpf = XPathFactory.newInstance();
            XPath xpath = xpf.newXPath();
            NodeList datasourceNodes = (NodeList) xpath.evaluate("DataSource", m_configElement,XPathConstants.NODESET);
            if(datasourceNodes == null) return ;
            for(int i=0;i<datasourceNodes.getLength();i++)
            {
	             Node dsNode = datasourceNodes.item(i);
	             Properties properties = getProperties(dsNode);
	             if("false".equals(properties.getProperty("enable")) || properties.getProperty("name")==null) continue;
	             DataSource datasource = BasicDataSourceFactory.createDataSource(properties);
	             ds_jdni_datasource_Map.put(properties.getProperty("name"),datasource);
			} 
		}
		catch(Exception e)
		{
			
		}

	}
	
	/**
	 *  获取一个数据库连接，该连接不会自动关闭，使用之后请及时关闭连接
	 * @param name 数据源的JNDI名称
	 * @return
	 * @throws DBConnectionException 
	 */
	public Connection getConnection(String name) throws SQLException
	{
		Connection conn = null;
		try
		{
			//首先从MAP中获取DataSource实例
			DataSource ds = (DataSource)ds_jdni_datasource_Map.get(name);
			
			//如果找不到，则从应用服务器中获取，然后放入MAP中
			if(ds == null)
			{
				if(m_dsp != null && m_context != null)
				{
					ds = m_dsp.getDataSource(m_context, name);
					ds_jdni_datasource_Map.put(name,ds);
				}
			}
			
			//通过DataSource实例获取Connection实例
			conn = (ds == null) ? null : ds.getConnection();
			DBConnectionThreadLocal.addConnection(conn);
		}
		catch (NamingException e)
		{
			throw new RuntimeException(e);
		}
		return conn;
	}
}
